/*
 * Serial Studio - https://serial-studio.github.io/
 *
 * Copyright (C) 2020-2025 Alex Spataru <https://aspatru.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later OR Commercial
 */

#include "JSON/Dataset.h"

/**
 * @brief Reads a value from a QJsonObject based on a key, returning a default
 *        value if the key does not exist.
 *
 * This function checks if the given key exists in the provided QJsonObject.
 * If the key is found, it returns the associated value. Otherwise, it returns
 * the specified default value.
 *
 * @param object The QJsonObject to read the data from.
 * @param key The key to look for in the QJsonObject.
 * @param defaultValue The value to return if the key is not found in the
 * QJsonObject.
 * @return The value associated with the key, or the defaultValue if the key is
 * not present.
 */
static QVariant SAFE_READ(const QJsonObject &object, const QString &key,
                          const QVariant &defaultValue)
{
  if (object.contains(key))
    return object.value(key);

  return defaultValue;
}

/**
 * @brief Constructor function, initializes default values
 */
JSON::Dataset::Dataset(const int groupId, const int datasetId)
  : m_fft(false)
  , m_led(false)
  , m_log(false)
  , m_graph(false)
  , m_title("")
  , m_value("")
  , m_units("")
  , m_widget("")
  , m_index(0)
  , m_max(0)
  , m_min(0)
  , m_alarm(0)
  , m_ledHigh(1)
  , m_fftSamples(256)
  , m_fftSamplingRate(100)
  , m_groupId(groupId)
  , m_xAxisId(-1)
  , m_datasetId(datasetId)
{
}

/**
 * @return @c true if the UI should generate a FFT plot of this dataset
 */
bool JSON::Dataset::fft() const
{
  return m_fft;
}

/**
 * @return @c true if the UI should generate a LED of this dataset
 */
bool JSON::Dataset::led() const
{
  return m_led;
}

/**
 * @return @c true if the UI should generate a logarithmic plot of this dataset
 */
bool JSON::Dataset::log() const
{
  return m_log;
}

/**
 * @return the field index represented by the current dataset
 */
int JSON::Dataset::index() const
{
  return m_index;
}

/**
 * @return @c true if the UI should graph this dataset
 */
bool JSON::Dataset::graph() const
{
  return m_graph;
}

/**
 * Returns the minimum value of the dataset
 */
double JSON::Dataset::min() const
{
  return m_min;
}

/**
 * Returns the maximum value of the dataset
 */
double JSON::Dataset::max() const
{
  return m_max;
}

/**
 * Returns the alarm level of the dataset
 */
double JSON::Dataset::alarm() const
{
  return m_alarm;
}

/**
 * Returns the LED active threshold value.
 */
double JSON::Dataset::ledHigh() const
{
  return m_ledHigh;
}

/**
 * @return The title/description of this dataset
 */
const QString &JSON::Dataset::title() const
{
  return m_title;
}

/**
 * @return The value/reading of this dataset
 */
const QString &JSON::Dataset::value() const
{
  return m_value;
}

/**
 * @return The units of this dataset
 */
const QString &JSON::Dataset::units() const
{
  return m_units;
}

/**
 * @return The widget value of this dataset
 */
const QString &JSON::Dataset::widget() const
{
  return m_widget;
}

/**
 * @return The frame index for the data source for the x-axis, -1 when the
 *         x axis data should be automatically generated by Serial Studio.
 */
int JSON::Dataset::xAxisId() const
{
  return m_xAxisId;
}

/**
 * Returns the maximum freq. for the FFT transform
 */
int JSON::Dataset::fftSamples() const
{
  return qMax(1, m_fftSamples);
}

/**
 * Returns the sampling rate for the FFT transform
 */
int JSON::Dataset::fftSamplingRate() const
{
  return m_fftSamplingRate;
}

/**
 * @return The index of the group to which the dataset belongs to, used by
 *         the project model to easily identify which group/dataset to update
 *         when the user modifies a parameter in the project model.
 */
int JSON::Dataset::groupId() const
{
  return m_groupId;
}

/**
 * @return The index of the dataset relative to the available datasets of the
 *         parent group.
 */
int JSON::Dataset::datasetId() const
{
  return m_datasetId;
}

/**
 * Returns the JSON data that represents this widget
 */
const QJsonObject &JSON::Dataset::jsonData() const
{
  return m_jsonData;
}

/**
 * @brief Encodes the dataset information into a QJsonObject.
 *
 * This function serializes the dataset's properties into a JSON object.
 * Note that the "m_value" field is deliberately excluded from the encoding
 * process.
 *
 * @return A QJsonObject containing the dataset's properties.
 */
QJsonObject JSON::Dataset::serialize() const
{
  QJsonObject object;
  object.insert(QStringLiteral("fft"), m_fft);
  object.insert(QStringLiteral("led"), m_led);
  object.insert(QStringLiteral("log"), m_log);
  object.insert(QStringLiteral("min"), m_min);
  object.insert(QStringLiteral("max"), m_max);
  object.insert(QStringLiteral("index"), m_index);
  object.insert(QStringLiteral("alarm"), m_alarm);
  object.insert(QStringLiteral("graph"), m_graph);
  object.insert(QStringLiteral("xAxis"), m_xAxisId);
  object.insert(QStringLiteral("ledHigh"), m_ledHigh);
  object.insert(QStringLiteral("fftSamples"), m_fftSamples);
  object.insert(QStringLiteral("value"), m_value.simplified());
  object.insert(QStringLiteral("title"), m_title.simplified());
  object.insert(QStringLiteral("units"), m_units.simplified());
  object.insert(QStringLiteral("widget"), m_widget.simplified());
  object.insert(QStringLiteral("fftSamplingRate"), m_fftSamplingRate);
  return object;
}

/**
 * Reads dataset information from the given @a object.
 *
 * @return @c true on read success, @c false on failure
 */
bool JSON::Dataset::read(const QJsonObject &object)
{
  if (!object.isEmpty())
  {
    m_min = SAFE_READ(object, "min", 0).toDouble();
    m_max = SAFE_READ(object, "max", 0).toDouble();
    m_index = SAFE_READ(object, "index", 0).toInt();
    m_fft = SAFE_READ(object, "fft", false).toBool();
    m_led = SAFE_READ(object, "led", false).toBool();
    m_log = SAFE_READ(object, "log", false).toBool();
    m_alarm = SAFE_READ(object, "alarm", 0).toDouble();
    m_graph = SAFE_READ(object, "graph", false).toBool();
    m_ledHigh = SAFE_READ(object, "ledHigh", 0).toDouble();
    m_fftSamples = SAFE_READ(object, "fftSamples", 256).toInt();
    m_title = SAFE_READ(object, "title", "").toString().simplified();
    m_value = SAFE_READ(object, "value", "").toString().simplified();
    m_units = SAFE_READ(object, "units", "").toString().simplified();
    m_widget = SAFE_READ(object, "widget", "").toString().simplified();
    m_fftSamplingRate = SAFE_READ(object, "fftSamplingRate", 100).toInt();
    if (m_value.isEmpty())
      m_value = QStringLiteral("--.--");

#ifdef COMMERCIAL_BUILD
    m_xAxisId = SAFE_READ(object, "xAxis", 0).toInt();
#else
    m_xAxisId = -1;
#endif

    return true;
  }

  return false;
}
